
require 'appwiki/base/base_controller.rb'
require 'appwiki/user/user.rb'
require 'appwiki/note/note.rb'
#require 'appwiki/group/group.rb'

#require_gem 'ruby-openid'

#
# User Management
#
# See http://www.aidanf.net/rails_user_authentication_tutorial
# And http://cs.nerdbank.net/blogs/jmpinline/archive/2006/06/03/243.aspx
#
# There is a slight barnacle in this code for supporting 'Notes':
# Users and Notes share the same namespace and we want to disallow creation of users where notes already exist (and visa versa).
#

class UserController < BaseController

  def whereami() [__FILE__] end

  #
  # user should be the found leaf
  # 
  def preload
    @current_user = nil
    @current_user = User.find_by_id(@session[:userid]) if @session[:userid]
    @user = nil
    @user = User.find_named(@workingpath.last) if @workingpath && @workingpath.length
    @user = nil if @user && !@user.verified
    @user = nil if @user && @user.login && @user.login.length < 1 # xxx debug test
    @visitingself = false
    @visitingself = true if @user && @session[:userid] && @session[:userid] == @user.id
  end

  # default fallback action for requests that are unresolved
  def default
    flash[:warning] = "Sorry, I can't resolve this request"
    render :action => :error
  end

  # show user
  def index
    profile
  end

  #
  # show all users
  #
  def all
    @results = User.find(:all,
                         :offset => 0,
                         :limit => 999,
                         :order => "created_at desc" )
    render :action => :all
  end

  # show all users
  def people
    all
  end

  # show all users
  def participants
    all
  end

  # display a profile
  def profile
    if @user
      render :action => :profile
      return
    end
    flash[:warning] = "There doesn't appear to be a user at this name"
    render :action => :error
  end

  # groups... arguable if this should be here or not. xxx
  #def groups
  #  @results = Group.get_memberships(@user);
  #end

  #
  # edit a user
  #
  def edit
    if !@user || !@current_user || @current_user.id != @user.id
      flash[:warning] = "You're not able to edit this user"
      render :action => "error"
      return
    end
    if @method=='post'
      d = @params[:depiction] # ugh xxx
      @params.delete(:depiction)
      @params.delete('depiction')
      @user.set(params)
      @params[:depiction] = d
      if @user.save
        if @params[:depiction]
          depiction,@err = BaseUtils.save_file({
            :site=>@domainreversed,
            :blob=>@params[:depiction],
            :name=>@user.login,
            :thumb=>true
            })
          @user.depiction = depiction if depiction
          @user.save
          if !@user.depiction
            #flash[:warning] = "Sorry, failed to save your depiction! #{@err}"
            #render :action => :edit
            #return
          end
        end
        redirect_to :action => ''
        return
      end
      errors = ''
      @user.errors.each {|a,b| errors = "#{errors}<br/>#{a}, #{b}" } 
      flash[:warning] = "Edit unsuccessful due to #{errors}"
    end
    render :action => "edit"
  end
  
  def edit_one
    if !@user || !@current_user || @current_user.id != @user.id
      flash[:warning] = "You're not able to edit this user"
      render :action => "error"
      return
    end
    if @method=='post'
     @user.set(params)
      if @user.save
        redirect_to :action => ''
        return
      end
      errors = ''
      @user.errors.each {|a,b| errors = "#{errors}<br/>#{a}, #{b}" } 
      flash[:warning] = "Edit unsuccessful due to #{errors}"      
    end
    render :action => "edit_one"
  end

  #
  # new user event; finalize user
  #
  def event_new_user(nuser)
    return if !nuser
    @nuser = nuser
    session[:userid] = @nuser.id
    session[:username] = @nuser.login
    redirect_to :url => "/#{session[:username]}"
  end

  #
  # signup
  #
  def signup

    # build up a temporary representation; does not save
    @nuser = nil
    begin
      #@nuser = User.new(@params)
      @nuser = User.new
      @nuser.set(@params)
    rescue
      flash[:warning] = "Could not make account"
      render :action => :error
      return
    end

    # deal with saving
    if @method == 'post' && @nuser && @nuser.login

      # do a manual check for similar names - and block (case insensitive)
      @similar = nil
      @similar = User.find_named(@nuser.login)
      if @similar
        flash[:warning] = "Sorry this user name is taken, you will have to try pick another one"
        render :action => :signup
        return
      end

      # check against notes also
      if Note.find_by_uid(@nuser.login)
        flash[:warning] = "Sorry this name is taken, you will have to try pick another one"
        render :action => :signup
        return
      end

      @nuser.uid = @nuser.login
      @nuser.verified = 123
      if @nuser.save
        #Note.make_path(@nuser,[@nuser.login])
        @nuser = User.authenticate(@nuser.login, @nuser.password)
        if @nuser
          event_new_user(@nuser)
          return
        end
        flash[:warning] = "Signup ok but login unsuccessful for reasons unknown"
      else
        errors = ''
        @nuser.errors.each { |alpha,beta| errors = "#{errors}<br/>#{alpha}, #{beta}" } 
        flash[:warning] = "<strong>SIGNUP UNSUCCESSFUL DUE TO: </strong>#{errors}"
      end
    end
    render :action => "signup"
  end
  
  #
  # confirm page after parent signs kid up
  #
  def parent_confirm
  end

  #
  # login
  # 
  def login
    if session[:username]
      redirect_to :url => "/"+session[:username]
      return
    end
    if @method == 'post'
      @user = User.authenticate(params[:login], params[:password])
      if @user
        session[:userid] = @user.id
        session[:username] = @user.login
        session.save
        flash[:message]  = "Login successful"
        #if @params[:referer] && @params[:referer].length > 1
        #  redirect_to :url => @params[:referer]
        #else
        redirect_to :url => "/#{session[:username]}"
        #end
        return
      else
        flash[:warning] = "Login unsuccessful"
      end
    end
    render :action => "login"
  end

  #
  # logout
  # 
  def logout
    @user = nil
    session[:userid] = nil
    session[:username] = nil
    session.save
    redirect_to :url => "/"
  end

  #
  # forgot password
  #
  def forgot_password
    if @method == 'post'
      u= User.find_by_email(params[:email])
      if u and u.send_new_password
        mess = "From: noreply@youthgive.org\n" +
               "To: #{email}\n" +
               "\n" +
               "You have been given a new password: #{u.password}\n" +
               "http://youthgive.org/#{login}/confirm?key=#{password}"
        Net::SMTP.start('localhost',25) do |smtp|
          smtp.send_message mess,"noreply@youthgive.org",email
        end
        flash[:message]  = "Password has been sent by email."
        redirect_to :action=> 'login'
      else
        flash[:warning]  = "Couldn't send password"
      end
    end
  end

  #
  # change password
  #
  def change_password
    if !@user
      redirect_to :action => :login
      return
    end
    @user = User.find_by_id(session[:userid])
    if @method == 'post'
      @user.update_attributes(
           :password => params[:password], 
           :password_confirmation => params[:password_confirmation])
      if @user.save
        flash[:message]="Password Changed"
      end
    end
  end

  #
  # login openid
  #
  def openid_login
    if session[:username]
      redirect_to :url => "/#{session[:username]}"
      return
    end
    openid_url = @params[:openid_url]
    if @method == 'post'
      @user = User.find_by_openid_url(openid_url)
      if !@user
        flash[:warning] = "You need to sign up first"
        redirect_to :action => "openid_signup"
        return
      end
      login = @user.login
      openid_consumer = OpenID::Consumer.new(@session,OpenID::FilesystemStore.new("openid"))
      openid_request = openid_consumer.begin(openid_url)
      case openid_request.status
      when OpenID::SUCCESS
        target = openid_request.redirect_url("http://*.#{@domainbase}","http://#{@domainbase}/#{login}/openid_complete")
        puts "openid_login() visiting #{target}" if @@debug
        redirect_to :url => target
        return
      when OpenID::FAILURE
        puts "openid_login() failure 1" if @@debug
        flash[:warning] = "OpenID login failed"
        render :action => :error
        return
      else
        puts "openid_login() failure 2" if @@debug
        flash[:warning] = "OpenID login failure"
        render :action => :error
        return
      end
    end
    render :action => :openid_login
  end

  # trim
  def openid_trim(openid_url)
    return nil if !openid_url
    if openid_url.index("http://") == 0
      openid_url = openid_url[7..-1]
    end
    if openid_url[-1..-1] == "/"
      openid_url = openid_url[0..-2]
    end
    puts "openid_signup() trimmed #{openid_url}" if @@debug
    return openid_url
  end

  #
  # signup openid
  #
  def openid_signup
    if session[:username]
      redirect_to :url => "/#{session[:username]}"
      return
    end
    openid_url = openid_trim(@params[:openid_url])
    @params[:openid_url] = openid_url
    @params[:password] = 'garbage'
    @params[:password_confirmation] = 'garbage'
    @nuser = User.new(@params)
    if @method == 'post'

      # do a manual check for similar names - and block (case insensitive)
      @similar = nil
      @similar = User.find_named(@nuser.login)
      if @similar
        flash[:warning] = "Sorry this user name is taken, you will have to try pick another one"
        render :action => "openid_signup"
        return
      end

      # check against notes also
      if Note.find_by_uid(@nuser.login)
        flash[:warning] = "Sorry this name is taken, you will have to try pick another one"
        render :action => "openid_signup"
        return
      end

      @nuser.uid = @nuser.login
      @nuser.verified = 123;
      if @nuser.save
        #Note.make_path(@nuser,[@nuser.login])
        login = @nuser.login
        openid_consumer = OpenID::Consumer.new(@session,OpenID::FilesystemStore.new("openid"))
        openid_request = openid_consumer.begin(openid_url)
        case openid_request.status
        when OpenID::SUCCESS
          target = openid_request.redirect_url("http://*.#{@domainbase}","http://#{@domainbase}/#{login}/openid_complete")
          puts "openid_login() visiting #{target}" if @@debug
          redirect_to :url => target
          return
        when OpenID::FAILURE
          puts "openid_login() failure 1" if @@debug
          flash[:warning] = "OpenID login failed"
          render :action => "error"
          return
        else
          puts "openid_login() failure 2" if @@debug
          flash[:warning] = "OpenID login failure"
          render :action => "error"
          return
        end
      else
        @nuser.errors.each { |alpha,beta| puts "#{alpha}, #{beta}" } 
        flash[:warning] = "Signup unsuccessful due to #{@user.errors}"
      end
    end
    render :action => "openid_signup"
  end

  def openid_complete
    openid_consumer = OpenID::Consumer.new(@session,OpenID::FilesystemStore.new("openid"))
    openid_response = openid_consumer.complete(@params)
    case openid_response.status
    when OpenID::SUCCESS
      puts "openid_login(): response is #{openid_response.identity_url}" if @@debug
      openid_url = openid_trim(openid_response.identity_url)
      @user = User.find_by_openid_url(openid_url)
      if @user
        session[:userid] = @user.id
        session[:username] = @user.login
        session.save
        flash[:message] = "Signup successful"
        redirect_to :url => "/#{session[:username]}"
        return
      end
      flash[:warning] = "Signup ok but login unsuccessful for reasons unknown"
      render :action => "error"     
      return
    when OpenID::FAILURE
      flash[:warning] = "Failed"
      render :action => "error"     
      return
    when OpenID::CANCEL
      flash[:warning] = "User cancelled"
      render :action => "error"     
      return
    else
    end
    flash[:warning] = "Unknown response from openid server"
    render :action => "error"     
  end

  #
  # confirm a lost password or provisional user
  #
  def confirm

    # must have a key
    if !params[:key]
      redirect_to :url => "/"
      return
    end

    # see if it is a valid user at all
    @user = nil
    @user = User.find_named(@workingpath.last) if @workingpath
    if !@user
      flash[:warning] = "Could not find this user - internal error"
      render :action => :error
     return
    end

    # test against this key
    @user = nil
    @user = User.authenticate(@workingpath.last, params[:key]) if @workingpath
    if !@user
      flash[:warning] = "Could not authenticate this user - internal error"
      render :action => :error
      return
    end

    # log them in?
    if @method == 'post'
      @user.update_attributes(
           :password => params[:password], 
           :password_confirmation => params[:password_confirmation])
      @user.verified = 123
      if @user.save
        session[:userid] = @user.id
        session[:username] = @user.login
        session.save
        redirect_to :url => "/#{@user.login}/edit"
      else
        errors = ''
        @user.errors.each do |alpha,beta|
          errors = "#{errors}<br/>#{alpha}, #{beta}"  
        end
        flash[:warning] = "Signup unsuccessful due to #{errors}"
        @user = nil
      end
    end

    render :action => :confirm
  end

end
